package top.superflow.db.orientdb

import java.io.{File, StringWriter}
import java.util.jar.JarFile

import com.orientechnologies.orient.server.config.{OServerConfiguration, OServerConfigurationManager, OServerEntryConfiguration, OServerUserConfiguration}
import javax.xml.bind.{JAXBContext, Marshaller}
import top.superflow.constant.GlobalConst
import top.superflow.db.api.DBServerConfiguration
import top.superflow.exception.{DBServerErrorCode, DBServerErrorMsg, DBServerException}
import top.superflow.lang.StringUtils
import top.superflow.log.Loggable
import top.superflow.utils.{ClasspathUtil, FileUtils, JarUtils, SuperflowENVUtil}

case class OrientServerConfiguration(path : Any) extends DBServerConfiguration with Loggable{
  
  /**
   * orient db configuration 
   * http://orientdb.com/docs/master/Configuration.html?q=
   */
  private var configPath : File = _
  
  private var serverConfig : OServerConfiguration = _
  path match {
    case p : String => {
      logger.info("The configured DB configuration path is {}" , p)
      this.configPath = new File(ClasspathUtil.retrieveRealPathFromClasspath(p))
      setServerConfig(this.configPath)
    }
    case filePath : File => {
      this.configPath = filePath
      setServerConfig(this.configPath)
    }
    case _ => {
      throw new DBServerException(DBServerErrorCode.CONFIGURATION_WRONG_INPUT, DBServerErrorMsg.CONFIGURATION_WRONG_INPUT)
    }
  }
  
  logger.info("The actual configuration file path OrientDB is {}" , this.configPath)
  
  private def setServerConfig (configPath : File ) = {
    this.serverConfig = new OServerConfigurationManager(configPath).getConfiguration
    setDBServerHome(this.serverConfig)
    setDBServerRootUser(this.serverConfig)
    setPluginDirectory(this.serverConfig)

    initOrientDBStudio()
  }

  private def initOrientDBStudio(): Unit ={
    val allJarFiles = JarUtils.getAllJarFiles
    val orientDBStudioJar = getJarFileForOrientDBStudio(allJarFiles)

    if(orientDBStudioJar == null){
      return
    }

    FileUtils.copy(orientDBStudioJar.getName, getOrientPluginDir)
  }

  private def getJarFileForOrientDBStudio(allJarFiles: Set[JarFile]): JarFile ={
    var jarFile : JarFile = null
    for(jf <- allJarFiles){
      if(StringUtils.containsIgnoreCase(jf.getName, "orientdb") && StringUtils.containsIgnoreCase(jf.getName, "studio")){
        jarFile = jf
      }
    }

    return jarFile
  }
  private def setPluginDirectory(orientDBConfiguration : OServerConfiguration){
    addProperty(orientDBConfiguration,"plugin.hotReload", "true")
    addProperty(orientDBConfiguration,"plugin.dynamic", "true")
    addProperty(orientDBConfiguration,"plugin.loadAtStartup", "true")
    addProperty(orientDBConfiguration, "plugin.directory", getOrientPluginDir)
  }

  private def getOrientPluginDir = {
    SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_ENV_VAR_DB_HOME) + "/orientdb/plugins"
  }

  private def addProperty(orientDBConfiguration: OServerConfiguration, propName: String, propValue:String): Unit ={
    val property : OServerEntryConfiguration = new OServerEntryConfiguration();
    property.name = propName
    property.value = propValue

    addProperty(orientDBConfiguration, property)
  }

  private def addProperty(orientDBConfiguration: OServerConfiguration, dbServerLocation: OServerEntryConfiguration) = {
    if (this.serverConfig.properties == null || this.serverConfig.properties.size == 0) {
      orientDBConfiguration.properties = Array(dbServerLocation)
    } else {
      orientDBConfiguration.properties = this.serverConfig.properties ++ Array(dbServerLocation)
    }
  }

  private def setDBServerHome(orientDBConfiguration : OServerConfiguration){
    val dbServerLocation : OServerEntryConfiguration = new OServerEntryConfiguration();
    dbServerLocation.name = "server.database.path"
    dbServerLocation.value = SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_ENV_VAR_DB_HOME)
    
    addProperty(this.serverConfig, dbServerLocation)
  }
  
  private def setDBServerRootUser(orientDBConfiguration : OServerConfiguration){
    val dbServerRootUser : OServerUserConfiguration = new OServerUserConfiguration();
    dbServerRootUser.name = SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_DB_CONFIG_DEFAULT_USER_NAME)
    dbServerRootUser.password = SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_DB_CONFIG_DEFAULT_USER_PASSWORD)
    dbServerRootUser.resources = SuperflowENVUtil.getAssuredProperty(GlobalConst.SYSTEM_DB_CONFIG_DEFAULT_USER_RESOURCES)


    if(this.serverConfig.users == null || this.serverConfig.users.size == 0){
      this.serverConfig.users = Array(dbServerRootUser)
    }else{
      this.serverConfig.users = this.serverConfig.users ++ Array(dbServerRootUser)
    }
  }
  
  
  def getConfiguration : OServerConfiguration = {
    this.serverConfig
  }
  
  def getConfigAsString = {
    val  jaxbContext: JAXBContext = JAXBContext.newInstance(this.serverConfig.getClass);
    val jaxbMarshaller:Marshaller  = jaxbContext.createMarshaller();

		// output pretty printed
		jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

		val out = new StringWriter
		jaxbMarshaller.marshal(this.serverConfig, out)
		out.toString()
  }
  
  
  
}